using DotNet.Utilities.ConsoleHelper;
using PropertyChanged;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;
using WPFPractice.Utils;
using WPFTemplateLib.WpfHelpers;

namespace WPFPractice.Windows
{
    public partial class WinCSharpTester : Window
    {
        private CSharpTesterViewModel _vm;

        public WinCSharpTester()
        {
            InitializeComponent();
            DataContext = _vm = new CSharpTesterViewModel();
        }
    }

    [AddINotifyPropertyChangedInterface]
    public class CSharpTesterViewModel : MyViewModelBase
    {
        #region 成员

        #endregion

        #region Bindable

        #endregion

        #region Command

        /// <summary>
        /// 命令：各种表达式
        /// </summary>
        public ICommand ManyExpressionCmd { get; set; }
        
        /// <summary>
        /// 命令：抛出异常
        /// </summary>
        public ICommand ThrowExceptionCmd { get; set; }

        /// <summary>
        /// 命令：测试扩展方法的场景
        /// </summary>
        public ICommand ExtensionTestCmd { get; set; }

        #endregion

        public CSharpTesterViewModel()
        {
            SetCommandMethod();

            InitValue();

            Console.SetOut(new ConsoleWriter(s =>
            {
                ShowInfo(s);
            }));
        }

        /// <summary>
        /// 数据初始化
        /// </summary>
        private void InitValue()
        {
            ShowInfo("演示 C# 语言功能");
        }

        /// <summary>
        /// 命令方法赋值(在构造函数中调用)
        /// </summary>
        private void SetCommandMethod()
        {
            ManyExpressionCmd ??= new RelayCommand(o => true, o =>
            {
                Console.WriteLine($"演示各种表达式");
                var a = 6;
                var b = true;
                CheckExpression(true);
                CheckExpression(b);
                CheckExpression(a > 5);
            });
            
            ThrowExceptionCmd ??= new RelayCommand(o => true, o =>
            {
                Console.WriteLine($"演示参数不符合条件时抛出异常");
                try
                {
                    Operation(null);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"捕获异常：{ex}");
                }
            });

            ExtensionTestCmd ??= new RelayCommand(o => true, o => 
            {
                Console.WriteLine($"演示获取扩展方法的调用表达式");
                try
                {
                    var sequence = Enumerable.Range(0, 10).GetNewSequence(100);
                    var list = sequence.ToList();
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"捕获异常：{ex}");
                }
            });
        }

        #region Method
        
        /// <summary>
        /// 调用委托前检查是否为 null
        /// </summary>
        /// <param name="func">委托方法</param>
        private void Operation(Action func)
        {
            MethodHelper.ValidateArgument(nameof(func), func is not null);
            func();
        }

        /// <summary>
        /// 检查（输出）表达式
        /// </summary>
        /// <param name="condition">条件表达式</param>
        /// <param name="message">消息（无需填写，自动获取表达式）</param>
        private void CheckExpression(bool condition, [CallerArgumentExpression("condition")] string? message = null)
        {
            Console.WriteLine($"Condition: {message}");
        }

        #endregion
    }

    public static class ExtensionTester
    {
        /// <summary>
        /// 获取新的序列（以给定间隔对序列进行采样，如果序列中包含的元素少于间隔数，则会报错）
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="sequence">序列</param>
        /// <param name="frequency">频率（间隔数）</param>
        /// <param name="message">（获取调用方参数表达式）</param>
        /// <returns>采样后的序列</returns>
        public static IEnumerable<T> GetNewSequence<T>(this IEnumerable<T> sequence, int frequency,
            [CallerArgumentExpression("sequence")] string? message = null)
        {
            if (sequence.Count() < frequency)
                throw new ArgumentException($"Expression doesn't have enough elements: {message}", nameof(sequence));

            int i = 0;
            foreach (T item in sequence)
            {
                if (i++ % frequency == 0)
                    yield return item;
            }
        }
    }
}
